/*
 * contrib/hstore/hstore_op.c
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "common/hashfn.h"
#include "funcapi.h"
#include "hstore.h"
#include "utils/builtins.h"
#include "utils/memutils.h"

/* C函数的旧名称 */
HSTORE_POLLUTE(hstore_fetchval, fetchval);
HSTORE_POLLUTE(hstore_exists, exists);
HSTORE_POLLUTE(hstore_defined, defined);
HSTORE_POLLUTE(hstore_delete, delete);
HSTORE_POLLUTE(hstore_concat, hs_concat);
HSTORE_POLLUTE(hstore_contains, hs_contains);
HSTORE_POLLUTE(hstore_contained, hs_contained);
HSTORE_POLLUTE(hstore_akeys, akeys);
HSTORE_POLLUTE(hstore_avals, avals);
HSTORE_POLLUTE(hstore_skeys, skeys);
HSTORE_POLLUTE(hstore_svals, svals);
HSTORE_POLLUTE(hstore_each, each);


/*
 * 我们经常需要以升序查找一个键序列。
 * “lowbound”参数用于在调用之间缓存搜索的下界，基于这个假设。
 * 对于一次性或无序搜索，请传递NULL。
 */
int hstoreFindKey(HStore *fc_hs, int *fc_lowbound, char *fc_key, int fc_keylen)
{
	HEntry	   *fc_entries = ARRPTR(fc_hs);
	int			fc_stopLow = fc_lowbound ? *fc_lowbound : 0;
	int			fc_stopHigh = HS_COUNT(fc_hs);
	int			fc_stopMiddle;
	char	   *fc_base = STRPTR(fc_hs);

	while (fc_stopLow < fc_stopHigh)
	{
		int			fc_difference;

		fc_stopMiddle = fc_stopLow + (fc_stopHigh - fc_stopLow) / 2;

		if (HSTORE_KEYLEN(fc_entries, fc_stopMiddle) == fc_keylen)
			fc_difference = memcmp(HSTORE_KEY(fc_entries, fc_base, fc_stopMiddle), fc_key, fc_keylen);
		else
			fc_difference = (HSTORE_KEYLEN(fc_entries, fc_stopMiddle) > fc_keylen) ? 1 : -1;

		if (fc_difference == 0)
		{
			if (fc_lowbound)
				*fc_lowbound = fc_stopMiddle + 1;
			return fc_stopMiddle;
		}
		else if (fc_difference < 0)
			fc_stopLow = fc_stopMiddle + 1;
		else
			fc_stopHigh = fc_stopMiddle;
	}

	if (fc_lowbound)
		*fc_lowbound = fc_stopLow;
	return -1;
}

Pairs *
hstoreArrayToPairs(ArrayType *fc_a, int *fc_npairs)
{
	Datum	   *fc_key_datums;
	bool	   *fc_key_nulls;
	int			fc_key_count;
	Pairs	   *fc_key_pairs;
	int			fc_bufsiz;
	int			fc_i,
				fc_j;

	deconstruct_array(fc_a,
					  TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_key_datums, &fc_key_nulls, &fc_key_count);

	if (fc_key_count == 0)
	{
		*fc_npairs = 0;
		return NULL;
	}

	/*
	 * 文本数组每个元素至少使用八个字节，因此“key_count * sizeof(Pairs)”中的任何溢出都足够小，可以被palloc()捕获。
	 * 然而，对数组格式的可行改进可能会使该假设失效。因此，使用显式检查而不是依赖palloc()来抱怨。
	 */
	if (fc_key_count > MaxAllocSize / sizeof(Pairs))
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
						fc_key_count, (int) (MaxAllocSize / sizeof(Pairs)))));

	fc_key_pairs = palloc(sizeof(Pairs) * fc_key_count);

	for (fc_i = 0, fc_j = 0; fc_i < fc_key_count; fc_i++)
	{
		if (!fc_key_nulls[fc_i])
		{
			fc_key_pairs[fc_j].key = VARDATA(fc_key_datums[fc_i]);
			fc_key_pairs[fc_j].keylen = VARSIZE(fc_key_datums[fc_i]) - VARHDRSZ;
			fc_key_pairs[fc_j].val = NULL;
			fc_key_pairs[fc_j].vallen = 0;
			fc_key_pairs[fc_j].needfree = 0;
			fc_key_pairs[fc_j].isnull = 1;
			fc_j++;
		}
	}

	*fc_npairs = hstoreUniquePairs(fc_key_pairs, fc_j, &fc_bufsiz);

	return fc_key_pairs;
}


PG_FUNCTION_INFO_V1(hstore_fetchval);
Datum hstore_fetchval(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	text	   *fc_key = PG_GETARG_TEXT_PP(1);
	HEntry	   *fc_entries = ARRPTR(fc_hs);
	text	   *fc_out;
	int			fc_idx = hstoreFindKey(fc_hs, NULL,
									VARDATA_ANY(fc_key), VARSIZE_ANY_EXHDR(fc_key));

	if (fc_idx < 0 || HSTORE_VALISNULL(fc_entries, fc_idx))
		PG_RETURN_NULL();

	fc_out = cstring_to_text_with_len(HSTORE_VAL(fc_entries, STRPTR(fc_hs), fc_idx),
								   HSTORE_VALLEN(fc_entries, fc_idx));

	PG_RETURN_TEXT_P(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_exists);
Datum hstore_exists(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	text	   *fc_key = PG_GETARG_TEXT_PP(1);
	int			fc_idx = hstoreFindKey(fc_hs, NULL,
									VARDATA_ANY(fc_key), VARSIZE_ANY_EXHDR(fc_key));

	PG_RETURN_BOOL(fc_idx >= 0);
}


PG_FUNCTION_INFO_V1(hstore_exists_any);
Datum hstore_exists_any(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	ArrayType  *fc_keys = PG_GETARG_ARRAYTYPE_P(1);
	int			fc_nkeys;
	Pairs	   *fc_key_pairs = hstoreArrayToPairs(fc_keys, &fc_nkeys);
	int			fc_i;
	int			fc_lowbound = 0;
	bool		fc_res = false;

	/*
	 * 我们利用对pairs列表已经严格按升序排序的事实，来缩小hstoreFindKey搜索的范围；每次搜索可以从上一个“找到”的条目之后一个条目开始，或在上一次搜索的下界开始。
	 */
	for (fc_i = 0; fc_i < fc_nkeys; fc_i++)
	{
		int			fc_idx = hstoreFindKey(fc_hs, &fc_lowbound,
										fc_key_pairs[fc_i].key, fc_key_pairs[fc_i].keylen);

		if (fc_idx >= 0)
		{
			fc_res = true;
			break;
		}
	}

	PG_RETURN_BOOL(fc_res);
}


PG_FUNCTION_INFO_V1(hstore_exists_all);
Datum hstore_exists_all(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	ArrayType  *fc_keys = PG_GETARG_ARRAYTYPE_P(1);
	int			fc_nkeys;
	Pairs	   *fc_key_pairs = hstoreArrayToPairs(fc_keys, &fc_nkeys);
	int			fc_i;
	int			fc_lowbound = 0;
	bool		fc_res = true;

	/*
	 * 我们利用对pairs列表已经严格按升序排序的事实，来缩小hstoreFindKey搜索的范围；每次搜索可以从上一个“找到”的条目之后一个条目开始，或在上一次搜索的下界开始。
	 */
	for (fc_i = 0; fc_i < fc_nkeys; fc_i++)
	{
		int			fc_idx = hstoreFindKey(fc_hs, &fc_lowbound,
										fc_key_pairs[fc_i].key, fc_key_pairs[fc_i].keylen);

		if (fc_idx < 0)
		{
			fc_res = false;
			break;
		}
	}

	PG_RETURN_BOOL(fc_res);
}


PG_FUNCTION_INFO_V1(hstore_defined);
Datum hstore_defined(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	text	   *fc_key = PG_GETARG_TEXT_PP(1);
	HEntry	   *fc_entries = ARRPTR(fc_hs);
	int			fc_idx = hstoreFindKey(fc_hs, NULL,
									VARDATA_ANY(fc_key), VARSIZE_ANY_EXHDR(fc_key));
	bool		fc_res = (fc_idx >= 0 && !HSTORE_VALISNULL(fc_entries, fc_idx));

	PG_RETURN_BOOL(fc_res);
}


PG_FUNCTION_INFO_V1(hstore_delete);
Datum hstore_delete(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	text	   *fc_key = PG_GETARG_TEXT_PP(1);
	char	   *fc_keyptr = VARDATA_ANY(fc_key);
	int			fc_keylen = VARSIZE_ANY_EXHDR(fc_key);
	HStore	   *fc_out = palloc(VARSIZE(fc_hs));
	char	   *fc_bufs,
			   *fc_bufd,
			   *fc_ptrd;
	HEntry	   *fc_es,
			   *fc_ed;
	int			fc_i;
	int			fc_count = HS_COUNT(fc_hs);
	int			fc_outcount = 0;

	SET_VARSIZE(fc_out, VARSIZE(fc_hs));
	HS_SETCOUNT(fc_out, fc_count);	/* 暂时! */

	fc_bufs = STRPTR(fc_hs);
	fc_es = ARRPTR(fc_hs);
	fc_bufd = fc_ptrd = STRPTR(fc_out);
	fc_ed = ARRPTR(fc_out);

	for (fc_i = 0; fc_i < fc_count; ++fc_i)
	{
		int			fc_len = HSTORE_KEYLEN(fc_es, fc_i);
		char	   *fc_ptrs = HSTORE_KEY(fc_es, fc_bufs, fc_i);

		if (!(fc_len == fc_keylen && memcmp(fc_ptrs, fc_keyptr, fc_keylen) == 0))
		{
			int			fc_vallen = HSTORE_VALLEN(fc_es, fc_i);

			HS_COPYITEM(fc_ed, fc_bufd, fc_ptrd, fc_ptrs, fc_len, fc_vallen,
						HSTORE_VALISNULL(fc_es, fc_i));
			++fc_outcount;
		}
	}

	HS_FINALIZE(fc_out, fc_outcount, fc_bufd, fc_ptrd);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_delete_array);
Datum hstore_delete_array(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	HStore	   *fc_out = palloc(VARSIZE(fc_hs));
	int			fc_hs_count = HS_COUNT(fc_hs);
	char	   *fc_ps,
			   *fc_bufd,
			   *fc_pd;
	HEntry	   *fc_es,
			   *fc_ed;
	int			fc_i,
				fc_j;
	int			fc_outcount = 0;
	ArrayType  *fc_key_array = PG_GETARG_ARRAYTYPE_P(1);
	int			fc_nkeys;
	Pairs	   *fc_key_pairs = hstoreArrayToPairs(fc_key_array, &fc_nkeys);

	SET_VARSIZE(fc_out, VARSIZE(fc_hs));
	HS_SETCOUNT(fc_out, fc_hs_count); /* 暂时! */

	fc_ps = STRPTR(fc_hs);
	fc_es = ARRPTR(fc_hs);
	fc_bufd = fc_pd = STRPTR(fc_out);
	fc_ed = ARRPTR(fc_out);

	if (fc_nkeys == 0)
	{
		/* 返回输入的副本，不变 */
		memcpy(fc_out, fc_hs, VARSIZE(fc_hs));
		HS_FIXSIZE(fc_out, fc_hs_count);
		HS_SETCOUNT(fc_out, fc_hs_count);
		PG_RETURN_POINTER(fc_out);
	}

	/*
	 * 这实际上是hs和key_pairs之间的合并，这两者都已经按(keylen,key)排序；我们仅从hs中提取键。
	 */

	for (fc_i = fc_j = 0; fc_i < fc_hs_count;)
	{
		int			fc_difference;

		if (fc_j >= fc_nkeys)
			fc_difference = -1;
		else
		{
			int			fc_skeylen = HSTORE_KEYLEN(fc_es, fc_i);

			if (fc_skeylen == fc_key_pairs[fc_j].keylen)
				fc_difference = memcmp(HSTORE_KEY(fc_es, fc_ps, fc_i),
									fc_key_pairs[fc_j].key,
									fc_key_pairs[fc_j].keylen);
			else
				fc_difference = (fc_skeylen > fc_key_pairs[fc_j].keylen) ? 1 : -1;
		}

		if (fc_difference > 0)
			++fc_j;
		else if (fc_difference == 0)
			++fc_i, ++fc_j;
		else
		{
			HS_COPYITEM(fc_ed, fc_bufd, fc_pd,
						HSTORE_KEY(fc_es, fc_ps, fc_i), HSTORE_KEYLEN(fc_es, fc_i),
						HSTORE_VALLEN(fc_es, fc_i), HSTORE_VALISNULL(fc_es, fc_i));
			++fc_outcount;
			++fc_i;
		}
	}

	HS_FINALIZE(fc_out, fc_outcount, fc_bufd, fc_pd);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_delete_hstore);
Datum hstore_delete_hstore(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	HStore	   *fc_hs2 = PG_GETARG_HSTORE_P(1);
	HStore	   *fc_out = palloc(VARSIZE(fc_hs));
	int			fc_hs_count = HS_COUNT(fc_hs);
	int			fc_hs2_count = HS_COUNT(fc_hs2);
	char	   *fc_ps,
			   *fc_ps2,
			   *fc_bufd,
			   *fc_pd;
	HEntry	   *fc_es,
			   *fc_es2,
			   *fc_ed;
	int			fc_i,
				fc_j;
	int			fc_outcount = 0;

	SET_VARSIZE(fc_out, VARSIZE(fc_hs));
	HS_SETCOUNT(fc_out, fc_hs_count); /* 暂时! */

	fc_ps = STRPTR(fc_hs);
	fc_es = ARRPTR(fc_hs);
	fc_ps2 = STRPTR(fc_hs2);
	fc_es2 = ARRPTR(fc_hs2);
	fc_bufd = fc_pd = STRPTR(fc_out);
	fc_ed = ARRPTR(fc_out);

	if (fc_hs2_count == 0)
	{
		/* 返回输入的副本，不变 */
		memcpy(fc_out, fc_hs, VARSIZE(fc_hs));
		HS_FIXSIZE(fc_out, fc_hs_count);
		HS_SETCOUNT(fc_out, fc_hs_count);
		PG_RETURN_POINTER(fc_out);
	}

	/*
	 * 这实际上是hs和hs2之间的合并，这两者都已经按(keylen,key)排序；我们仅从hs中提取键；对于相等的键，我们取hs中的值，除非值相等。
	 */

	for (fc_i = fc_j = 0; fc_i < fc_hs_count;)
	{
		int			fc_difference;

		if (fc_j >= fc_hs2_count)
			fc_difference = -1;
		else
		{
			int			fc_skeylen = HSTORE_KEYLEN(fc_es, fc_i);
			int			fc_s2keylen = HSTORE_KEYLEN(fc_es2, fc_j);

			if (fc_skeylen == fc_s2keylen)
				fc_difference = memcmp(HSTORE_KEY(fc_es, fc_ps, fc_i),
									HSTORE_KEY(fc_es2, fc_ps2, fc_j),
									fc_skeylen);
			else
				fc_difference = (fc_skeylen > fc_s2keylen) ? 1 : -1;
		}

		if (fc_difference > 0)
			++fc_j;
		else if (fc_difference == 0)
		{
			int			fc_svallen = HSTORE_VALLEN(fc_es, fc_i);
			int			fc_snullval = HSTORE_VALISNULL(fc_es, fc_i);

			if (fc_snullval != HSTORE_VALISNULL(fc_es2, fc_j) ||
				(!fc_snullval && (fc_svallen != HSTORE_VALLEN(fc_es2, fc_j) ||
							   memcmp(HSTORE_VAL(fc_es, fc_ps, fc_i),
									  HSTORE_VAL(fc_es2, fc_ps2, fc_j),
									  fc_svallen) != 0)))
			{
				HS_COPYITEM(fc_ed, fc_bufd, fc_pd,
							HSTORE_KEY(fc_es, fc_ps, fc_i), HSTORE_KEYLEN(fc_es, fc_i),
							fc_svallen, fc_snullval);
				++fc_outcount;
			}
			++fc_i, ++fc_j;
		}
		else
		{
			HS_COPYITEM(fc_ed, fc_bufd, fc_pd,
						HSTORE_KEY(fc_es, fc_ps, fc_i), HSTORE_KEYLEN(fc_es, fc_i),
						HSTORE_VALLEN(fc_es, fc_i), HSTORE_VALISNULL(fc_es, fc_i));
			++fc_outcount;
			++fc_i;
		}
	}

	HS_FINALIZE(fc_out, fc_outcount, fc_bufd, fc_pd);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_concat);
Datum hstore_concat(PG_FUNCTION_ARGS)
{
	HStore	   *fc_s1 = PG_GETARG_HSTORE_P(0);
	HStore	   *fc_s2 = PG_GETARG_HSTORE_P(1);
	HStore	   *fc_out = palloc(VARSIZE(fc_s1) + VARSIZE(fc_s2));
	char	   *fc_ps1,
			   *fc_ps2,
			   *fc_bufd,
			   *fc_pd;
	HEntry	   *fc_es1,
			   *fc_es2,
			   *fc_ed;
	int			fc_s1idx;
	int			fc_s2idx;
	int			fc_s1count = HS_COUNT(fc_s1);
	int			fc_s2count = HS_COUNT(fc_s2);
	int			fc_outcount = 0;

	SET_VARSIZE(fc_out, VARSIZE(fc_s1) + VARSIZE(fc_s2) - HSHRDSIZE);
	HS_SETCOUNT(fc_out, fc_s1count + fc_s2count);

	if (fc_s1count == 0)
	{
		/* 返回输入的副本，不变 */
		memcpy(fc_out, fc_s2, VARSIZE(fc_s2));
		HS_FIXSIZE(fc_out, fc_s2count);
		HS_SETCOUNT(fc_out, fc_s2count);
		PG_RETURN_POINTER(fc_out);
	}

	if (fc_s2count == 0)
	{
		/* 返回输入的副本，不变 */
		memcpy(fc_out, fc_s1, VARSIZE(fc_s1));
		HS_FIXSIZE(fc_out, fc_s1count);
		HS_SETCOUNT(fc_out, fc_s1count);
		PG_RETURN_POINTER(fc_out);
	}

	fc_ps1 = STRPTR(fc_s1);
	fc_ps2 = STRPTR(fc_s2);
	fc_bufd = fc_pd = STRPTR(fc_out);
	fc_es1 = ARRPTR(fc_s1);
	fc_es2 = ARRPTR(fc_s2);
	fc_ed = ARRPTR(fc_out);

	/*
	 * 这实际上是s1和s2之间的合并，这两者都已经按(keylen,key)排序；对于相等的键，我们取s2。
	 */

	for (fc_s1idx = fc_s2idx = 0; fc_s1idx < fc_s1count || fc_s2idx < fc_s2count; ++fc_outcount)
	{
		int			fc_difference;

		if (fc_s1idx >= fc_s1count)
			fc_difference = 1;
		else if (fc_s2idx >= fc_s2count)
			fc_difference = -1;
		else
		{
			int			fc_s1keylen = HSTORE_KEYLEN(fc_es1, fc_s1idx);
			int			fc_s2keylen = HSTORE_KEYLEN(fc_es2, fc_s2idx);

			if (fc_s1keylen == fc_s2keylen)
				fc_difference = memcmp(HSTORE_KEY(fc_es1, fc_ps1, fc_s1idx),
									HSTORE_KEY(fc_es2, fc_ps2, fc_s2idx),
									fc_s1keylen);
			else
				fc_difference = (fc_s1keylen > fc_s2keylen) ? 1 : -1;
		}

		if (fc_difference >= 0)
		{
			HS_COPYITEM(fc_ed, fc_bufd, fc_pd,
						HSTORE_KEY(fc_es2, fc_ps2, fc_s2idx), HSTORE_KEYLEN(fc_es2, fc_s2idx),
						HSTORE_VALLEN(fc_es2, fc_s2idx), HSTORE_VALISNULL(fc_es2, fc_s2idx));
			++fc_s2idx;
			if (fc_difference == 0)
				++fc_s1idx;
		}
		else
		{
			HS_COPYITEM(fc_ed, fc_bufd, fc_pd,
						HSTORE_KEY(fc_es1, fc_ps1, fc_s1idx), HSTORE_KEYLEN(fc_es1, fc_s1idx),
						HSTORE_VALLEN(fc_es1, fc_s1idx), HSTORE_VALISNULL(fc_es1, fc_s1idx));
			++fc_s1idx;
		}
	}

	HS_FINALIZE(fc_out, fc_outcount, fc_bufd, fc_pd);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_slice_to_array);
Datum hstore_slice_to_array(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	HEntry	   *fc_entries = ARRPTR(fc_hs);
	char	   *fc_ptr = STRPTR(fc_hs);
	ArrayType  *fc_key_array = PG_GETARG_ARRAYTYPE_P(1);
	ArrayType  *fc_aout;
	Datum	   *fc_key_datums;
	bool	   *fc_key_nulls;
	Datum	   *fc_out_datums;
	bool	   *fc_out_nulls;
	int			fc_key_count;
	int			fc_i;

	deconstruct_array(fc_key_array,
					  TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_key_datums, &fc_key_nulls, &fc_key_count);

	if (fc_key_count == 0)
	{
		fc_aout = construct_empty_array(TEXTOID);
		PG_RETURN_POINTER(fc_aout);
	}

	fc_out_datums = palloc(sizeof(Datum) * fc_key_count);
	fc_out_nulls = palloc(sizeof(bool) * fc_key_count);

	for (fc_i = 0; fc_i < fc_key_count; ++fc_i)
	{
		text	   *fc_key = (text *) DatumGetPointer(fc_key_datums[fc_i]);
		int			fc_idx;

		if (fc_key_nulls[fc_i])
			fc_idx = -1;
		else
			fc_idx = hstoreFindKey(fc_hs, NULL, VARDATA(fc_key), VARSIZE(fc_key) - VARHDRSZ);

		if (fc_idx < 0 || HSTORE_VALISNULL(fc_entries, fc_idx))
		{
			fc_out_nulls[fc_i] = true;
			fc_out_datums[fc_i] = (Datum) 0;
		}
		else
		{
			fc_out_datums[fc_i] =
				PointerGetDatum(cstring_to_text_with_len(HSTORE_VAL(fc_entries, fc_ptr, fc_idx),
														 HSTORE_VALLEN(fc_entries, fc_idx)));
			fc_out_nulls[fc_i] = false;
		}
	}

	fc_aout = construct_md_array(fc_out_datums, fc_out_nulls,
							  ARR_NDIM(fc_key_array),
							  ARR_DIMS(fc_key_array),
							  ARR_LBOUND(fc_key_array),
							  TEXTOID, -1, false, TYPALIGN_INT);

	PG_RETURN_POINTER(fc_aout);
}


PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
Datum hstore_slice_to_hstore(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	HEntry	   *fc_entries = ARRPTR(fc_hs);
	char	   *fc_ptr = STRPTR(fc_hs);
	ArrayType  *fc_key_array = PG_GETARG_ARRAYTYPE_P(1);
	HStore	   *fc_out;
	int			fc_nkeys;
	Pairs	   *fc_key_pairs = hstoreArrayToPairs(fc_key_array, &fc_nkeys);
	Pairs	   *fc_out_pairs;
	int			fc_bufsiz;
	int			fc_lastidx = 0;
	int			fc_i;
	int			fc_out_count = 0;

	if (fc_nkeys == 0)
	{
		fc_out = hstorePairs(NULL, 0, 0);
		PG_RETURN_POINTER(fc_out);
	}

	/* hstoreArrayToPairs()检查溢出 */
	fc_out_pairs = palloc(sizeof(Pairs) * fc_nkeys);
	fc_bufsiz = 0;

	/*
	 * 我们利用对pairs列表已经严格按升序排序的事实，来缩小hstoreFindKey搜索的范围；每次搜索可以从上一个“找到”的条目之后一个条目开始，或在上一次搜索的下界开始。
	 */

	for (fc_i = 0; fc_i < fc_nkeys; ++fc_i)
	{
		int			fc_idx = hstoreFindKey(fc_hs, &fc_lastidx,
										fc_key_pairs[fc_i].key, fc_key_pairs[fc_i].keylen);

		if (fc_idx >= 0)
		{
			fc_out_pairs[fc_out_count].key = fc_key_pairs[fc_i].key;
			fc_bufsiz += (fc_out_pairs[fc_out_count].keylen = fc_key_pairs[fc_i].keylen);
			fc_out_pairs[fc_out_count].val = HSTORE_VAL(fc_entries, fc_ptr, fc_idx);
			fc_bufsiz += (fc_out_pairs[fc_out_count].vallen = HSTORE_VALLEN(fc_entries, fc_idx));
			fc_out_pairs[fc_out_count].isnull = HSTORE_VALISNULL(fc_entries, fc_idx);
			fc_out_pairs[fc_out_count].needfree = false;
			++fc_out_count;
		}
	}

	/*
	 * 我们在这里不使用hstoreUniquePairs，因为我们知道pairs列表已经排序且唯一。
	 */

	fc_out = hstorePairs(fc_out_pairs, fc_out_count, fc_bufsiz);

	PG_RETURN_POINTER(fc_out);
}


PG_FUNCTION_INFO_V1(hstore_akeys);
Datum hstore_akeys(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	Datum	   *fc_d;
	ArrayType  *fc_a;
	HEntry	   *fc_entries = ARRPTR(fc_hs);
	char	   *fc_base = STRPTR(fc_hs);
	int			fc_count = HS_COUNT(fc_hs);
	int			fc_i;

	if (fc_count == 0)
	{
		fc_a = construct_empty_array(TEXTOID);
		PG_RETURN_POINTER(fc_a);
	}

	fc_d = (Datum *) palloc(sizeof(Datum) * fc_count);

	for (fc_i = 0; fc_i < fc_count; ++fc_i)
	{
		text	   *fc_t = cstring_to_text_with_len(HSTORE_KEY(fc_entries, fc_base, fc_i),
												 HSTORE_KEYLEN(fc_entries, fc_i));

		fc_d[fc_i] = PointerGetDatum(fc_t);
	}

	fc_a = construct_array(fc_d, fc_count,
						TEXTOID, -1, false, TYPALIGN_INT);

	PG_RETURN_POINTER(fc_a);
}


PG_FUNCTION_INFO_V1(hstore_avals);
Datum hstore_avals(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	Datum	   *fc_d;
	bool	   *fc_nulls;
	ArrayType  *fc_a;
	HEntry	   *fc_entries = ARRPTR(fc_hs);
	char	   *fc_base = STRPTR(fc_hs);
	int			fc_count = HS_COUNT(fc_hs);
	int			fc_lb = 1;
	int			fc_i;

	if (fc_count == 0)
	{
		fc_a = construct_empty_array(TEXTOID);
		PG_RETURN_POINTER(fc_a);
	}

	fc_d = (Datum *) palloc(sizeof(Datum) * fc_count);
	fc_nulls = (bool *) palloc(sizeof(bool) * fc_count);

	for (fc_i = 0; fc_i < fc_count; ++fc_i)
	{
		if (HSTORE_VALISNULL(fc_entries, fc_i))
		{
			fc_d[fc_i] = (Datum) 0;
			fc_nulls[fc_i] = true;
		}
		else
		{
			text	   *fc_item = cstring_to_text_with_len(HSTORE_VAL(fc_entries, fc_base, fc_i),
														HSTORE_VALLEN(fc_entries, fc_i));

			fc_d[fc_i] = PointerGetDatum(fc_item);
			fc_nulls[fc_i] = false;
		}
	}

	fc_a = construct_md_array(fc_d, fc_nulls, 1, &fc_count, &fc_lb,
						   TEXTOID, -1, false, TYPALIGN_INT);

	PG_RETURN_POINTER(fc_a);
}


static ArrayType * fc_hstore_to_array_internal(HStore *fc_hs, int fc_ndims)
{
	HEntry	   *fc_entries = ARRPTR(fc_hs);
	char	   *fc_base = STRPTR(fc_hs);
	int			fc_count = HS_COUNT(fc_hs);
	int			fc_out_size[2] = {0, 2};
	int			fc_lb[2] = {1, 1};
	Datum	   *fc_out_datums;
	bool	   *fc_out_nulls;
	int			fc_i;

	Assert(fc_ndims < 3);

	if (fc_count == 0 || fc_ndims == 0)
		return construct_empty_array(TEXTOID);

	fc_out_size[0] = fc_count * 2 / fc_ndims;
	fc_out_datums = palloc(sizeof(Datum) * fc_count * 2);
	fc_out_nulls = palloc(sizeof(bool) * fc_count * 2);

	for (fc_i = 0; fc_i < fc_count; ++fc_i)
	{
		text	   *fc_key = cstring_to_text_with_len(HSTORE_KEY(fc_entries, fc_base, fc_i),
												   HSTORE_KEYLEN(fc_entries, fc_i));

		fc_out_datums[fc_i * 2] = PointerGetDatum(fc_key);
		fc_out_nulls[fc_i * 2] = false;

		if (HSTORE_VALISNULL(fc_entries, fc_i))
		{
			fc_out_datums[fc_i * 2 + 1] = (Datum) 0;
			fc_out_nulls[fc_i * 2 + 1] = true;
		}
		else
		{
			text	   *fc_item = cstring_to_text_with_len(HSTORE_VAL(fc_entries, fc_base, fc_i),
														HSTORE_VALLEN(fc_entries, fc_i));

			fc_out_datums[fc_i * 2 + 1] = PointerGetDatum(fc_item);
			fc_out_nulls[fc_i * 2 + 1] = false;
		}
	}

	return construct_md_array(fc_out_datums, fc_out_nulls,
							  fc_ndims, fc_out_size, fc_lb,
							  TEXTOID, -1, false, TYPALIGN_INT);
}

PG_FUNCTION_INFO_V1(hstore_to_array);
Datum hstore_to_array(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	ArrayType  *fc_out = fc_hstore_to_array_internal(fc_hs, 1);

	PG_RETURN_POINTER(fc_out);
}

PG_FUNCTION_INFO_V1(hstore_to_matrix);
Datum hstore_to_matrix(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	ArrayType  *fc_out = fc_hstore_to_array_internal(fc_hs, 2);

	PG_RETURN_POINTER(fc_out);
}

/*
 * 各种返回集合的函数的通用初始化函数。
 * 仅在函数返回复合类型时传递fcinfo；它将用于查找返回的tupledesc。
 * 我们在多次调用上下文中存储hstore的副本，以防它最初被压缩。（至少我猜这就是原因；
 * 原始代码中没有解释性注释。--AG）
 */

static void fc_setup_firstcall(FuncCallContext *fc_funcctx, HStore *fc_hs,
				FunctionCallInfo fc_fcinfo)
{
	MemoryContext fc_oldcontext;
	HStore	   *fc_st;

	fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

	fc_st = (HStore *) palloc(VARSIZE(fc_hs));
	memcpy(fc_st, fc_hs, VARSIZE(fc_hs));

	fc_funcctx->user_fctx = (void *) fc_st;

	if (fc_fcinfo)
	{
		TupleDesc	fc_tupdesc;

		/* 为我们的结果类型构建一个元组描述符 */
		if (get_call_result_type(fc_fcinfo, NULL, &fc_tupdesc) != TYPEFUNC_COMPOSITE)
			elog(ERROR, "return type must be a row type");

		fc_funcctx->tuple_desc = BlessTupleDesc(fc_tupdesc);
	}

	MemoryContextSwitchTo(fc_oldcontext);
}


PG_FUNCTION_INFO_V1(hstore_skeys);
Datum hstore_skeys(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	HStore	   *fc_hs;
	int			fc_i;

	if (SRF_IS_FIRSTCALL())
	{
		fc_hs = PG_GETARG_HSTORE_P(0);
		fc_funcctx = SRF_FIRSTCALL_INIT();
		fc_setup_firstcall(fc_funcctx, fc_hs, NULL);
	}

	fc_funcctx = SRF_PERCALL_SETUP();
	fc_hs = (HStore *) fc_funcctx->user_fctx;
	fc_i = fc_funcctx->call_cntr;

	if (fc_i < HS_COUNT(fc_hs))
	{
		HEntry	   *fc_entries = ARRPTR(fc_hs);
		text	   *fc_item;

		fc_item = cstring_to_text_with_len(HSTORE_KEY(fc_entries, STRPTR(fc_hs), fc_i),
										HSTORE_KEYLEN(fc_entries, fc_i));

		SRF_RETURN_NEXT(fc_funcctx, PointerGetDatum(fc_item));
	}

	SRF_RETURN_DONE(fc_funcctx);
}


PG_FUNCTION_INFO_V1(hstore_svals);
Datum hstore_svals(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	HStore	   *fc_hs;
	int			fc_i;

	if (SRF_IS_FIRSTCALL())
	{
		fc_hs = PG_GETARG_HSTORE_P(0);
		fc_funcctx = SRF_FIRSTCALL_INIT();
		fc_setup_firstcall(fc_funcctx, fc_hs, NULL);
	}

	fc_funcctx = SRF_PERCALL_SETUP();
	fc_hs = (HStore *) fc_funcctx->user_fctx;
	fc_i = fc_funcctx->call_cntr;

	if (fc_i < HS_COUNT(fc_hs))
	{
		HEntry	   *fc_entries = ARRPTR(fc_hs);

		if (HSTORE_VALISNULL(fc_entries, fc_i))
		{
			ReturnSetInfo *fc_rsi;

			/* 丑陋 丑陋 丑陋。为什么没有宏来处理这个？ */
			(fc_funcctx)->call_cntr++;
			fc_rsi = (ReturnSetInfo *) fcinfo->resultinfo;
			fc_rsi->isDone = ExprMultipleResult;
			PG_RETURN_NULL();
		}
		else
		{
			text	   *fc_item;

			fc_item = cstring_to_text_with_len(HSTORE_VAL(fc_entries, STRPTR(fc_hs), fc_i),
											HSTORE_VALLEN(fc_entries, fc_i));

			SRF_RETURN_NEXT(fc_funcctx, PointerGetDatum(fc_item));
		}
	}

	SRF_RETURN_DONE(fc_funcctx);
}


PG_FUNCTION_INFO_V1(hstore_contains);
Datum hstore_contains(PG_FUNCTION_ARGS)
{
	HStore	   *fc_val = PG_GETARG_HSTORE_P(0);
	HStore	   *fc_tmpl = PG_GETARG_HSTORE_P(1);
	bool		fc_res = true;
	HEntry	   *fc_te = ARRPTR(fc_tmpl);
	char	   *fc_tstr = STRPTR(fc_tmpl);
	HEntry	   *fc_ve = ARRPTR(fc_val);
	char	   *fc_vstr = STRPTR(fc_val);
	int			fc_tcount = HS_COUNT(fc_tmpl);
	int			fc_lastidx = 0;
	int			fc_i;

	/*
	 * 我们利用“tmpl”中的键按严格递增顺序排列的事实来缩小hstoreFindKey搜索；每次搜索可以从上一个“找到”的条目之后一个条目开始，或在搜索的下界开始。
	 */

	for (fc_i = 0; fc_res && fc_i < fc_tcount; ++fc_i)
	{
		int			fc_idx = hstoreFindKey(fc_val, &fc_lastidx,
										HSTORE_KEY(fc_te, fc_tstr, fc_i),
										HSTORE_KEYLEN(fc_te, fc_i));

		if (fc_idx >= 0)
		{
			bool		fc_nullval = HSTORE_VALISNULL(fc_te, fc_i);
			int			fc_vallen = HSTORE_VALLEN(fc_te, fc_i);

			if (fc_nullval != HSTORE_VALISNULL(fc_ve, fc_idx) ||
				(!fc_nullval && (fc_vallen != HSTORE_VALLEN(fc_ve, fc_idx) ||
							  memcmp(HSTORE_VAL(fc_te, fc_tstr, fc_i),
									 HSTORE_VAL(fc_ve, fc_vstr, fc_idx),
									 fc_vallen) != 0)))
				fc_res = false;
		}
		else
			fc_res = false;
	}

	PG_RETURN_BOOL(fc_res);
}


PG_FUNCTION_INFO_V1(hstore_contained);
Datum hstore_contained(PG_FUNCTION_ARGS)
{
	PG_RETURN_DATUM(DirectFunctionCall2(hstore_contains,
										PG_GETARG_DATUM(1),
										PG_GETARG_DATUM(0)
										));
}


PG_FUNCTION_INFO_V1(hstore_each);
Datum hstore_each(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	HStore	   *fc_hs;
	int			fc_i;

	if (SRF_IS_FIRSTCALL())
	{
		fc_hs = PG_GETARG_HSTORE_P(0);
		fc_funcctx = SRF_FIRSTCALL_INIT();
		fc_setup_firstcall(fc_funcctx, fc_hs, fcinfo);
	}

	fc_funcctx = SRF_PERCALL_SETUP();
	fc_hs = (HStore *) fc_funcctx->user_fctx;
	fc_i = fc_funcctx->call_cntr;

	if (fc_i < HS_COUNT(fc_hs))
	{
		HEntry	   *fc_entries = ARRPTR(fc_hs);
		char	   *fc_ptr = STRPTR(fc_hs);
		Datum		fc_res,
					fc_dvalues[2];
		bool		fc_nulls[2] = {false, false};
		text	   *fc_item;
		HeapTuple	fc_tuple;

		fc_item = cstring_to_text_with_len(HSTORE_KEY(fc_entries, fc_ptr, fc_i),
										HSTORE_KEYLEN(fc_entries, fc_i));
		fc_dvalues[0] = PointerGetDatum(fc_item);

		if (HSTORE_VALISNULL(fc_entries, fc_i))
		{
			fc_dvalues[1] = (Datum) 0;
			fc_nulls[1] = true;
		}
		else
		{
			fc_item = cstring_to_text_with_len(HSTORE_VAL(fc_entries, fc_ptr, fc_i),
											HSTORE_VALLEN(fc_entries, fc_i));
			fc_dvalues[1] = PointerGetDatum(fc_item);
		}

		fc_tuple = heap_form_tuple(fc_funcctx->tuple_desc, fc_dvalues, fc_nulls);
		fc_res = HeapTupleGetDatum(fc_tuple);

		SRF_RETURN_NEXT(fc_funcctx, PointerGetDatum(fc_res));
	}

	SRF_RETURN_DONE(fc_funcctx);
}


/*
 * btree的hstore排序顺序并不是为了有用；我们真的只关心相等与不相等。我们首先比较整个字符串缓冲区，然后比较条目位置数组。
 */

PG_FUNCTION_INFO_V1(hstore_cmp);
Datum hstore_cmp(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs1 = PG_GETARG_HSTORE_P(0);
	HStore	   *fc_hs2 = PG_GETARG_HSTORE_P(1);
	int			fc_hcount1 = HS_COUNT(fc_hs1);
	int			fc_hcount2 = HS_COUNT(fc_hs2);
	int			fc_res = 0;

	if (fc_hcount1 == 0 || fc_hcount2 == 0)
	{
		/*
		 * 如果任一操作数为空，且另一个非空，则非空的那个更大。如果两者均为空，则它们相等。
		 */
		if (fc_hcount1 > 0)
			fc_res = 1;
		else if (fc_hcount2 > 0)
			fc_res = -1;
	}
	else
	{
		/* 在这里我们知道两个操作数都是非空的 */
		char	   *fc_str1 = STRPTR(fc_hs1);
		char	   *fc_str2 = STRPTR(fc_hs2);
		HEntry	   *fc_ent1 = ARRPTR(fc_hs1);
		HEntry	   *fc_ent2 = ARRPTR(fc_hs2);
		size_t		fc_len1 = HSE_ENDPOS(fc_ent1[2 * fc_hcount1 - 1]);
		size_t		fc_len2 = HSE_ENDPOS(fc_ent2[2 * fc_hcount2 - 1]);

		fc_res = memcmp(fc_str1, fc_str2, Min(fc_len1, fc_len2));

		if (fc_res == 0)
		{
			if (fc_len1 > fc_len2)
				fc_res = 1;
			else if (fc_len1 < fc_len2)
				fc_res = -1;
			else if (fc_hcount1 > fc_hcount2)
				fc_res = 1;
			else if (fc_hcount2 > fc_hcount1)
				fc_res = -1;
			else
			{
				int			fc_count = fc_hcount1 * 2;
				int			fc_i;

				for (fc_i = 0; fc_i < fc_count; ++fc_i)
					if (HSE_ENDPOS(fc_ent1[fc_i]) != HSE_ENDPOS(fc_ent2[fc_i]) ||
						HSE_ISNULL(fc_ent1[fc_i]) != HSE_ISNULL(fc_ent2[fc_i]))
						break;
				if (fc_i < fc_count)
				{
					if (HSE_ENDPOS(fc_ent1[fc_i]) < HSE_ENDPOS(fc_ent2[fc_i]))
						fc_res = -1;
					else if (HSE_ENDPOS(fc_ent1[fc_i]) > HSE_ENDPOS(fc_ent2[fc_i]))
						fc_res = 1;
					else if (HSE_ISNULL(fc_ent1[fc_i]))
						fc_res = 1;
					else if (HSE_ISNULL(fc_ent2[fc_i]))
						fc_res = -1;
				}
			}
		}
		else
		{
			fc_res = (fc_res > 0) ? 1 : -1;
		}
	}

	/*
	 * 这是一个btree支持函数；这是少数几个需要显式释放内存的地方之一。
	 */
	PG_FREE_IF_COPY(fc_hs1, 0);
	PG_FREE_IF_COPY(fc_hs2, 1);
	PG_RETURN_INT32(fc_res);
}


PG_FUNCTION_INFO_V1(hstore_eq);
Datum hstore_eq(PG_FUNCTION_ARGS)
{
	int			fc_res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_res == 0);
}

PG_FUNCTION_INFO_V1(hstore_ne);
Datum hstore_ne(PG_FUNCTION_ARGS)
{
	int			fc_res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_res != 0);
}

PG_FUNCTION_INFO_V1(hstore_gt);
Datum hstore_gt(PG_FUNCTION_ARGS)
{
	int			fc_res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_res > 0);
}

PG_FUNCTION_INFO_V1(hstore_ge);
Datum hstore_ge(PG_FUNCTION_ARGS)
{
	int			fc_res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_res >= 0);
}

PG_FUNCTION_INFO_V1(hstore_lt);
Datum hstore_lt(PG_FUNCTION_ARGS)
{
	int			fc_res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_res < 0);
}

PG_FUNCTION_INFO_V1(hstore_le);
Datum hstore_le(PG_FUNCTION_ARGS)
{
	int			fc_res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
														PG_GETARG_DATUM(0),
														PG_GETARG_DATUM(1)));

	PG_RETURN_BOOL(fc_res <= 0);
}


PG_FUNCTION_INFO_V1(hstore_hash);
Datum hstore_hash(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	Datum		fc_hval = hash_any((unsigned char *) VARDATA(fc_hs),
								VARSIZE(fc_hs) - VARHDRSZ);

	/*
	 * 这是代码中唯一关心整体varlena大小是否恰好与真实数据大小匹配的地方之一；这个断言应该由所有其他代码维护，但我们在这里明确指出。
	 */
	Assert(VARSIZE(fc_hs) ==
		   (HS_COUNT(fc_hs) != 0 ?
			CALCDATASIZE(HS_COUNT(fc_hs),
						 HSE_ENDPOS(ARRPTR(fc_hs)[2 * HS_COUNT(fc_hs) - 1])) :
			HSHRDSIZE));

	PG_FREE_IF_COPY(fc_hs, 0);
	PG_RETURN_DATUM(fc_hval);
}

PG_FUNCTION_INFO_V1(hstore_hash_extended);
Datum hstore_hash_extended(PG_FUNCTION_ARGS)
{
	HStore	   *fc_hs = PG_GETARG_HSTORE_P(0);
	uint64		fc_seed = PG_GETARG_INT64(1);
	Datum		fc_hval;

	fc_hval = hash_any_extended((unsigned char *) VARDATA(fc_hs),
							 VARSIZE(fc_hs) - VARHDRSZ,
							 fc_seed);

	/* 请参见hstore_hash中的注释 */
	Assert(VARSIZE(fc_hs) ==
		   (HS_COUNT(fc_hs) != 0 ?
			CALCDATASIZE(HS_COUNT(fc_hs),
						 HSE_ENDPOS(ARRPTR(fc_hs)[2 * HS_COUNT(fc_hs) - 1])) :
			HSHRDSIZE));

	PG_FREE_IF_COPY(fc_hs, 0);
	PG_RETURN_DATUM(fc_hval);
}
